import serial
import modbus_tk
from modbus_tk import modbus_rtu
from modbus_tk.modbus_rtu import RtuMaster
import threading
import paho.mqtt.client as mqtt
import time
import json
import hmac
import base64


class ampere():
    def __init__(self, appId, appSecret, devName, nodeName, upload_protocol, upload_server, start_reg=86, port='/dev/ttyO1'):
        '''
        [summary]
        ampere为电流计数模块
        Args:
            name ([str]): [上级网关ID]
            start_reg ([int]): [该IN口的寄存器起始位,默认读八位]
            port (str, optional): [该IN口的485串口名,默认'/dev/ttyO1']. Defaults to '/dev/ttyO1'.
        '''
        self.start_reg = start_reg
        self.port = port
        self.upload_protocol = upload_protocol
        self.upload_server = upload_server
        self.appId = appId
        self.appSecret = appSecret
        self.nodeName = nodeName
        
        # self.upload_topic = gsn+'/0/data'  # data topic
        self.upload_topic = f"{appId}/{devName}/{nodeName}"
        self.interval = 5
        self.upload_flag = False
        self.serial_flag = False
        self.orign_json = {
            "items": [
                {
                    "ts": 123123123,
                    "value": 100
                }
            ]
        }

        try:
            self.connect_to_upload_server()
        except Exception as e:
            print(e.args[-1])
            print('please check your internet config and reboot')

        try:
            self.connect_to_serial()
        except Exception as e:
            print(e.args[-1])
        '''
        [变量解释]
            connection(RTU Master)：[用于连接后的操作，在未连接时为None]
            interval(double):[间隔时间]
        '''

    

    def connect_to_upload_server(self):
        '''
        [连接到上传服务器，例如mqtt/mqtts]
        self.upload_server格式：
        {
            "host":frankgu.club, #域名或ip
            "port":1883, #端口 mqtt为1883，mqtts为8883
            "heartbeat":60 #心跳包时间
        }
        '''
        # def on_connect(client, userdata, flags, rc):
        #
        def on_connect(client, userdata, flags, rc):
            print("Connection returned " + str(rc))
            '''
            连接返回码不成功 重连
            '''
            if str(rc) != '0':
                print('Connection return:', str(rc))
                print('----- upload server reconnect -----')
                print('----- wait for 5s -----')
                self.upload_flag = False
                time.sleep(5)
            else:
                self.upload_flag = True

        def on_disconnect(client, userdata, rc):
            '''
            连接断开 重连
            '''
            self.upload_flag = False
            print('Disconnect Detected')
            self.connect_to_upload_server()

        t = int(time.time()) - 3600*8
        self.clientId = f"{self.appId}|sign=hmacsha1,ts={t}|"
        password = hmac.new(self.appSecret.encode('utf-8'), f"{self.appId}/{t}".encode('utf-8'), "sha1")
        password = base64.b64encode(password.digest())
        self.up_client = mqtt.Client(self.clientId)
        self.up_client.username_pw_set(str(self.appId), password)
        host = self.upload_server['host']
        port = self.upload_server['port']
        heartbeat = self.upload_server['heartbeat']
        self.up_client.on_connect = on_connect
        self.up_client.on_disconnect = on_disconnect
        while self.upload_flag == False:
            try:
                if self.upload_protocol == 'mqtt':
                    self.up_client.connect(host, port, heartbeat)
                    # heartbeat_topic = self.gsn + '/0/heartbeat'  # heartbeat topic
                    # self.keep_heartbeat(heartbeat_topic, heartbeat)  # 自动发心跳包
                    print('mqtt is connected')
                    self.upload_flag = True
                elif self.upload_protocol == 'mqtts':
                    print('mqtts is not available now')
                    self.upload_flag = True
                else:
                    print('this protocol is not available now')
                    self.upload_flag = True
            except Exception as e:
                '''
                    连接失败 无返回码 重连
                '''
                print(e.args[-1])
                print('----- upload server reconnect -----')
                print('----- wait for 5s -----')
                time.sleep(5)
                continue

    def keep_heartbeat(self, topic, heartbeat):
        try:
            self.upload_action(topic, '1', 0)
        except Exception as e:
            print(e.args[-1])
            print('Please Check the server config and reboot')
        timer = threading.Timer(
            heartbeat, lambda: self.keep_heartbeat(topic, heartbeat))
        timer.start()

    def connect_to_serial(self):
        '''
        [连接函数]
            设置连接时的端口，波特率9600，读取的位数为8位，无校验位，停止位1
        '''
        while self.serial_flag == False:
            try:
                self.connection = modbus_rtu.RtuMaster(serial.Serial(port=self.port,
                                                                     baudrate=9600,
                                                                     bytesize=8,
                                                                     parity="N",
                                                                     stopbits=1,
                                                                     xonxoff=0))

                self.connection.set_timeout(5)
                self.connection.set_verbose(True)
                print('connect to data collector')
                self.serial_flag = True
            except Exception as err:
                '''
                    连接失败 重连
                '''
                print(err)
                print('----- serial reconnect -----')
                print('----- wait for 5s -----')
                time.sleep(5)
                self.serial_flag = False
                continue

    def upload_action(self, topic, payload, qos):
        if self.upload_protocol == 'mqtt':
            self.up_client.publish(topic, payload, qos)
            print('upload to ', topic, ' success')
        elif self.upload_protocol == 'mqtts':
            print('upload failed')
            print('mqtts is not available now')
        else:
            print('upload failed')
            print('this protocol is not available now')

    def get_data(self):
        '''
        [读值函数]
            获取IN口数值：设备地址为01，功能码为02即读取IN口输入状态，IN口名称，读取的位数
        '''
        if self.connection is None:
            self.connect_to_serial()
        data = self.connection.execute(1, 3, self.start_reg, 1)
        data = list(data)
        return data
        

    def start_count(self):
        '''
        [计数函数]
            若上次读值为0且本次读值为1，则计数值加1，并将计数值上传至服务器，每过0.01秒读一次值
        '''
        try:
            data = self.get_data()
        except Exception as e:
            '''
                读不到数据 重连
            '''
            data = []
            print(e.args[-1])
            print('Please Check the serial and Reboot')
            self.connect_to_serial()
        if data == []:
            pass
        else:
            its = int(time.time()) - 3600*8
            payload = self.orign_json

            payload["items"][0]["ts"] = its
            payload["items"][0]["value"] = "%.4f" % round(data[0]*10/10000, 4)

            # payload['iTs'] = its
            # payload['data']['iData'][0]['iValue'] = "%.4f" % round(
            #     data[0]*10/10000, 4)
            # payload['data']['iData'][1]['iValue'] = "%.2f" % round(data[1]*150/10000,2)
            # payload['data']['iData'][2]['iValue'] = "%.2f" % round(data[2]*150/10000,2)
            tmpstr = json.dumps(payload)
            print(tmpstr)
            try:
                self.upload_action(self.upload_topic, tmpstr, 0)
            except Exception as e:
                print(e.args[-1])
                print('Please Check the server config and reboot')
                return
        timer = threading.Timer(self.interval, lambda: self.start_count())
        timer.start()


if __name__ == "__main__":
    print('----- wait for 5s -----')
    time.sleep(5)
    print('----- START -----')
    upload_protocol = 'mqtt'
    with open('/home/root/Ampere/upload_server.json', 'r') as f:
        upload_server = json.load(f)
    counthub = ampere(
        "pow1014", "8053688w", "pow1014", "pows14", upload_protocol=upload_protocol, upload_server=upload_server)  # 可加port参数，默认为/dev/ttyO1(大写的欧一 不是零一)
    counthub.start_count()
